<?php

declare(strict_types=1);
/**
 * 模型字段服务类
 * Author: Zane
 * Email: 873934580@qq.com
 * Date: 2023/6/19.
 */

namespace App\Service;

use App\Exceptions\CommonExceptionConstants;
use App\Exceptions\ServiceException;
use App\Models\ChannelField;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Zane\TableHandle\TableHandle;

class ChannelFieldService
{
    /**
     * 操作批量保存字段信息.
     *
     * @param array $data
     * @param $id
     */
    public function store(array $data, $id): void
    {
        $this->checkField($data);
        $ids = data_get($data, '*.id', []);
        $results = $this->byModelId($id);

        // 删除数据
        foreach ($results as $result) {
            if (!\in_array($result['id'], $ids, true)) {
                $result->delete();
            }
        }
        $weight = 255;
        ChannelModelService::ensureTableExists($id);
        foreach ($data as $datum) {
            $datum['weight'] = $weight;
            $datum['channel_model_id'] = $id;
            // 需要更新的操作
            if (isset($datum['id']) && $datum['id']) {
                $this->edit($datum, $datum['id']);

                continue;
            }
            // 新增操作
            $this->save($datum);

            $weight = --$datum['weight'];
        }
    }

    public function byModelId($modelId)
    {
        return ChannelField::query()->where('channel_model_id', $modelId)->get();
    }

    /**
     * @param mixed $id
     *
     * @throws \Exception
     */
    public function del($id): void
    {
        $dao = ChannelField::byId($id);
        if (isSystemField($dao->field)) {
            throw new ServiceException(CommonExceptionConstants::SYSTEM_FIELD_BAN);
        }
        $dao->delete();
    }

    /**
     * 删除字段，需要已经软删除的数据才能进行字段删除.
     *
     * @param $id
     */
    public function delField($id): void
    {
        $dao = ChannelField::withTrashed()->where('id', $id)->first();
        if (!$dao) {
            throw new ServiceException(CommonExceptionConstants::NO_FIND_DATA);
        }
        $table = getTableName($dao->parentModel->table_name);
        $fieldName = $dao->name;

        // 彻底删除
        Schema::table($table, function (Blueprint $table) use ($fieldName): void {
            $table->dropColumn($fieldName);
        });

        $dao->forceDelete();
    }

    private function save($data): void
    {
        DB::beginTransaction();

        try {
            $model = ChannelField::query()->create($data);
            TableHandle::make($model->type)->setModel($model)->handle();

            DB::commit();
        } catch (\Exception $e) {
            // 如果数据表字段创建失败测需要直接删除对应的字段信息
            DB::rollBack();

            throw new ServiceException(['message' => $e->getMessage()]);
        }
    }

    private function edit($data, $id): void
    {
        $model = ChannelField::byId($id);
        $oldModel = clone $model;
        if (isSystemField($model->field) && $data['field'] !== $model->field) {
            throw new ServiceException(CommonExceptionConstants::SYSTEM_FIELD_BAN);
        }
        $data = Arr::except($data, ['created_at', 'updated_at']);

        DB::beginTransaction();

        try {
            $model->update($data);
            if (TableHandle::existsFieldName(getTableName($model->parentModel->table_name), $oldModel->field)) {
                TableHandle::make($model->type)->setModel($model)->setOrigin($oldModel)->handle();
            } else {
                TableHandle::make($model->type)->setModel($model)->handle();
            }
            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();

            throw new ServiceException(['message' => $e->getMessage()]);
        }
    }

    /**
     * 验证字段是否出现重复的情况.
     *
     * @param $data
     */
    private function checkField($data): void
    {
        $field = [];
        foreach ($data as $datum) {
            if (isset($datum['field']) && $datum['field']) {
                if (\in_array($datum['field'], $field, true)) {
                    throw new ServiceException(['message' => '重复出现的字段名称【'.$datum['field'].'】,请调整后提交']);
                }
                $field[] = $datum['field'];
            }
        }
    }
}
